package maple.lang;

import java.lang.IllegalStateException;
import java.lang.IndexOutOfBoundsException;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.TreeMap;
import java.util.Vector;

import jade.domain.FIPAAgentManagement.Property;
import jade.domain.FIPAAgentManagement.ServiceDescription;

import maple.util.Actuator;
import maple.util.Context;
import maple.util.Profile;
import maple.util.Sensor;
import maple.util.Service;
import maple.util.tasks.ApplicationTask;
import maple.util.tasks.MasterTask;
import maple.util.tasks.GatewayTask;

public class Command extends Object 
{
	public static enum Message{
								OK,
							    BAD_COMMAND,
								ERROR,
							    EXCEPTION_FIPA,
							    EXCEPTION_IO,
							    EXCEPTION_PHIDGET,
							    NOT_FOUND
							  };
	
	protected boolean _available;
	protected Language.Instruction _instruction;
	protected TreeMap< String, String > _paramterss;

	/**
	 * construct a new command with the specified content
	 *
	 * @param	content	INSTRUCTION[ KEY="VALUE"]+
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public Command( final String content )
	{
		_instruction = null;
		_paramterss = new TreeMap< String, String >();

		try
		{
			Matcher matcher = Pattern.compile( Language.STATEMENT ).matcher( content );
			if( matcher.lookingAt() )
			{
				_instruction = Language.Instruction.valueOf( matcher.group( 1 ) );
				matcher = Pattern.compile( Language.EXPRESSION ).matcher( matcher.group( 2 ) );
				while( matcher.find() )
				{
					_paramterss.put( matcher.group( 1 ), matcher.group( 2 ) );
					//System.out.println( matcher.group( 1 ) );
					//System.out.println( matcher.group( 2 ) );
				}
			}
		}
		catch( IllegalArgumentException e )
		{
		}
		catch( IllegalStateException e )
		{
		}
		catch( IndexOutOfBoundsException e )
		{
		}

		_available = isValid();
	}

	/**
	 * construct a new command with the specified instruction and parameters
	 *
	 * @param	_instruction	the specified _instruction
	 * @param	_paramterss	the specified _paramterss
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public Command( final Language.Instruction _instruction, final TreeMap< String, String > _paramterss )
	{
		this._instruction = _instruction;
		this._paramterss = _paramterss;

		_available = isValid();
	}

	/**
	 * return the _instruction of this command or null if this command is not available
	 *
	 * @return	the _instruction of this command
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public Language.Instruction getInstruction()
	{
		if( _available )
		{
			return _instruction;
		}
		return null;
	}

	/**
	 * return the value to which the specified parameters is mapped in this command or null if this command is not available
	 *
	 * @param	_paramters	the specified _paramters
	 * @return	the value of the specified _paramters in this command
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public String getParameter( final String _paramters )
	{
		if( _available )
		{
			return _paramterss.get( _paramters );
		}
		return null;
	}

	/**
	 * return the parameters in this command or null if this command is not available
	 *
	 * @return	the _paramterss in this command
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public Vector< String > getParameters()
	{
		if( _available )
		{
			return new Vector< String >( _paramterss.keySet() );
		}
		return null;
	}

	/**
	 * return true if this command is _available
	 *
	 * @return	true if this task is _available
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public boolean isAvailable()
	{
		return _available;
	}

	/**
	 * return true if this task is valid 
	 *
	 * @return	true if this task is valid 
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	private boolean isValid()
	{
		if( _instruction != null )
		{
			switch( _instruction )
			{
				case INSERT: case DELETE: case UPDATE:
					for( Profile.Property property : Profile.Property.values() )
					{
						if( !_paramterss.containsKey( property.toString() ) )
						{
							return false;
						}
					}
					break;
				case READ: case WRITE:
					for( GatewayTask.Property property : GatewayTask.Property.values() )
					{
						if( !_paramterss.containsKey( property.toString() ) )
						{
							return false;
						}
					}
					break;
				case SET:
					for( MasterTask.Property property : MasterTask.Property.values() )
					{
						if( !_paramterss.containsKey( property.toString() ) )
						{
							return false;
						}
					}
					break;
				case INTERPRET:
					for( String property : _paramterss.keySet() )
					{
						try
						{
							Context.Type.valueOf( property );
						}
						catch( IllegalArgumentException ec )
						{
							try
							{
								Actuator.Type.valueOf( property );
							}
							catch( IllegalArgumentException ea )
							{
								try
								{
									Sensor.Type.valueOf( property );
								}
								catch( IllegalArgumentException es )
								{
									return false;
								}
							}
						}
					}
					break;
				case SUBSCRIBE:
					for( ApplicationTask.Property property : ApplicationTask.Property.values() )
					{
						if( !_paramterss.containsKey( property.toString() ) )
						{
							return false;
						}
					}
					break;
			}
			return true;
		}
		return false;
	}

	/**
	 * return true if the specified service description matches this command
	 *
	 * @param	service	the specified service description
	 * @return	true if the service description matches this available command
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public boolean match( final ServiceDescription service )
	{
		if( _available )
		{
			// extract the properties
			TreeMap< String, String > properties = new TreeMap< String, String >();
			Iterator iter = service.getAllProperties();
			while( iter.hasNext() )
			{
				Property property = ( Property ) iter.next();
				properties.put( property.getName(), ( String ) property.getValue() );
			}

			// match the properties
			for( String _paramters : _paramterss.keySet() )
			{
				String value = _paramterss.get( _paramters );
				if( _paramters.equals( Service.Property.NAME.toString() ) )
				{
					if( !service.getName().equals( value ) )
					{
						return false;
					}
				}
				else if( _paramters.equals( Service.Property.TYPE.toString() ) )
				{
					if( !service.getType().equals( value ) )
					{
						return false;
					}
				}
				else if( _paramters.equals( Service.Property.OWNERSHIP.toString() ) )
				{
					if( !service.getOwnership().equals( value ) )
					{
						return false;
					}
				}
				else
				{
					if( !properties.containsKey( _paramters ) )
					{
						return false;
					}
					else if( !properties.get( _paramters ).equals( value ) )
					{
						return false;
					}
				}
			}
			return true;
		}
		return false;
	}

	/**
	 * associate the specified value with the specified _paramters
	 *
	 * @param	_paramters	the specified _paramters
	 * @param	value		the value to be associated with the specified _paramters
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public void setParameter( final String _paramters, final String value )
	{
		if( _available )
		{
			_paramterss.put( _paramters, value );
		}
	}

	/**
	 * return the service description of this available SELECT command
	 *
	 * @return	the service description of this command 
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public ServiceDescription toService()
	{
		if( _available )
		{
			if( _instruction == Language.Instruction.SELECT )
			{
				ServiceDescription service = new ServiceDescription();
				service.addLanguages( Language.class.getName() );

				for( String _paramters : _paramterss.keySet() )
				{
					String value = _paramterss.get( _paramters );
					if( _paramters.equals( Service.Property.NAME.toString() ) )
					{
						service.setName( value );
					}
					else if( _paramters.equals( Service.Property.TYPE.toString() ) )
					{
						service.setType( value );
					}
					else if( _paramters.equals( Service.Property.OWNERSHIP.toString() ) )
					{
						service.setOwnership( value );
					}
					else
					{
						service.addProperties( new Property( _paramters, value ) );
					}
				}

				return service;
			}
		}
		return null;
	}

	/**
	 * return the String representation of this command or null if this command is not available
	 *
	 * @return	the String representation of this command
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public String toString()
	{
		if( _available )
		{
			String command = _instruction.toString();
			for( String _paramters : _paramterss.keySet() )
			{
				command = command 
						  + Language.DELIMITER
					      + _paramters
						  + Language.ASSIGN
						  + Language.QUOTE
						  + _paramterss.get( _paramters )
						  + Language.QUOTE;
			}
			return command;
		}
		return null;
	}
}
